<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="../css/reset.css">
    <style>
        #title {
            line-height: 40px;
        }

        .list li {
            height: 40px;
            line-height: 40px;
            border: 1px dashed #333;
        }

        .button-group {
            margin-top: 10px;
        }

        .active {
            background-color: red;
        }
    </style>
</head>

<body>
    <div class="wrap">
        <h3 id="title">DOM操作</h3>
        <ul class="list">
            <li>01</li>
            <li>02</li>
            <li>03</li>
            <li>04</li>
            <li>05</li>
            <li>06</li>
        </ul>
    </div>
    <div class="button-group">
        <button class="html">html</button>
        <button class="hide">hide</button>
        <button class="show">show</button>
        <button class="width">width</button>
        <button class="height">height</button>
        <button class="css">css</button>
        <button class="addClass">addClass</button>
        <button class="removeClass">removeClass</button>
        <button class="toggleClass">toggleClass</button>
        <button class="hasClass">hasClass</button>
        <button class="attr">attr</button>
        <button class="prop">prop</button>
    </div>
</body>

<script>

    // 传入一个css选择器(字符串) => 根据传入的选择器获取元素 => 返回元素的集合(实例化对象)
    function GetEle(selector) {
        // {}
        // {}[[Prototype]] = GetEle.prototype;
        // this -> {}
        var list = document.querySelectorAll(selector); // 伪数组

        list = Array.from(list); //先转为真数组  => 赋值原值
        Object.setPrototypeOf(list, GetEle.prototype); // 在修改真数组的原型属性([[Prototype]])

        return list; // [ele,ele,ele,ele]  构造函数返回自定义实例化对象
    }

    // html()  设置和获取元素的(实例化对象=>伪数组)html结构
    // html(123132)   有参数 => 就设置
    // html()         没有参数 => 就获取 
    GetEle.prototype.html = function (con) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        // arguments => 函数调用时实际参数的集合 
        if (arguments.length >= 1) { // 有至少一个参数

            //  this => 调用此方法的实例化对象 => 可以使用GetEle.prototype上所有的方法
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.innerHTML = con;
            // }
            this.each(function (ele, i) {
                ele.innerHTML = con;
            })
        } else { // 没有参数
            return this[0].innerHTML;
        }
    }

    // text()  设置和获取元素的(实例化对象=>伪数组)text结构
    // text(123132)   有参数 => 就设置
    // text()         没有参数 => 就获取 
    GetEle.prototype.text = function (con) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        // arguments => 函数调用时实际参数的集合 
        if (arguments.length >= 1) { // 有至少一个参数
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.innerText = con;
            // }

            this.each(function (ele, i) {
                ele.innerText = con;
            })
        } else { // 没有参数
            return this[0].innerText;
        }
    }


    // val()  设置和获取元素的(实例化对象=>伪数组)val结构
    // val(123132)   有参数 => 就设置
    // val()         没有参数 => 就获取 
    GetEle.prototype.val = function (con) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        // arguments => 函数调用时实际参数的集合 
        if (arguments.length >= 1) { // 有至少一个参数
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.value = con;
            // }

            this.each(function (ele, i) {
                ele.value = con;
            })
        } else { // 没有参数
            return this[0].value;
        }
    }


    // hide  show

    // hide()  隐藏集合(实例化对象)中的每一个元素
    GetEle.prototype.hide = function () {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        // for (var i = 0; i < this.length; i++) {
        //     var ele = this[i];
        //     ele.style.display = "none";
        // }
        this.each(function (ele, i) {
            ele.style.display = "none";
        })
    }

    // show()  显示集合(实例化对象)中的每一个元素
    GetEle.prototype.show = function () {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        // for (var i = 0; i < this.length; i++) {
        //     var ele = this[i];
        //     ele.style.display = "block";
        // }

        this.each(function (ele, i) {
            ele.style.display = "block";
        })
    }

    // on(type,callback)    给集合中的每个元素绑定事件
    // type 绑定事件的类型
    // callback  事件触发时执行的回调函数
    GetEle.prototype.on = function (type, callback) {
        // for (var i = 0; i < this.length; i++) {
        //     var ele = this[i];
        //     ele.addEventListener(type, callback);
        // }

        this.each(function (ele, i) {
            ele.addEventListener(type, callback);
        })
    }

    // off(type,callback)    对应回调函数,清除集合中的每个元素绑定的事件
    // type 绑定事件的类型
    // callback  事件触发时执行的回调函数
    GetEle.prototype.off = function (type, callback) {
        // for (var i = 0; i < this.length; i++) {
        //     var ele = this[i];
        //     ele.removeEventListener(type, callback);
        // }

        this.each(function (ele, i) {
            ele.removeEventListener(type, callback);
        })
    }

    // jquery中所有类型的事件 都是基于on()方法二次封装得到的
    GetEle.prototype.click = function (callback) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        this.on("click", callback);
    }
    GetEle.prototype.dblclick = function (callback) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        this.on("dblclick", callback);
    }
    GetEle.prototype.mousedown = function (callback) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        this.on("mousedown", callback);
    }
    GetEle.prototype.mousemove = function (callback) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        this.on("mousemove", callback);
    }
    GetEle.prototype.mouseup = function (callback) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        this.on("mouseup", callback);
    }

    // width()   获取和设置集合中元素(实例化对象)的宽度
    // width("200px")  
    // width()  
    GetEle.prototype.width = function (val) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        if (arguments.length >= 1) { //有参数
            this.each(function (ele, i) {
                // 判断是否有单位  
                // 有单位 直接赋值   => 1 px pt em rem % vw vh vmin vmax 
                // 没有单位 默认px
                var reg = /^\d+(px|pt|em|rem|%|vw|vh|vmin|vmax)$/;
                if (reg.test(val)) {
                    ele.style.width = val;
                } else {
                    ele.style.width = val + "px";
                }

            })
        } else { // 没有参数
            return parseInt(this.getStyle("width"));
        }
    }


    // height()   获取和设置集合中元素(实例化对象)的宽度
    // height("200px")  
    // height()  
    GetEle.prototype.height = function (val) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        if (arguments.length >= 1) { //有参数
            this.each(function (ele, i) {
                // 判断是否有单位  
                // 有单位 直接赋值   => 1 px pt em rem % vw vh vmin vmax 
                // 没有单位 默认px
                var reg = /^\d+(px|pt|em|rem|%|vw|vh|vmin|vmax)$/;
                if (reg.test(val)) {
                    ele.style.height = val;
                } else {
                    ele.style.height = val + "px";
                }

            })
        } else { // 没有参数
            return parseInt(this.getStyle("height"));
        }
    }

    // css()    获取和设置集合中元素(实例化对象)的css样式
    // css("width","200px")    有两个参数就设置
    // css("width");           有一个参数就获取

    GetEle.prototype.css = function (key, val) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        if (arguments.length >= 2) { // 有两个参数就设置
            this.each(function (ele, index) {
                ele.style[key] = val;
            })
        } else if (arguments.length == 1) { // 有一个参数

            if (typeof key == "string") {
                // css("width");  有一个参数,且是字符串 => 取值 => 默认返回第一个
                var ele = this[0];
                try {
                    return window.getComputedStyle(ele)[key];
                } catch (err) {
                    return ele.currentStyle[key];
                }
            } else if (typeof key == "object") {
                // css({background:"red",width:"200px"})  有一个参数,且是对象  => 获取键名和键值 设置
                for (var cssKey in key) {
                    var cssVal = key[cssKey];
                    this.css(cssKey, cssVal);
                }
            }


        } else {
            var err = new Error("Please pass in at least one parameter!");
            throw err;
        }
    }

    GetEle.prototype.getStyle = function (key) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        var ele = this[0];
        try {
            return window.getComputedStyle(ele)[key];
        } catch (err) {
            return ele.currentStyle[key];
        }
    }

    // addClass()      给集合中元素(实例化对象)添加一个或多个class名(多class名之间用空格分隔  "one two three")
    // removeClass()   删除集合中元素(实例化对象)一个或多个class名(多class名之间用空格分隔  "one two three")
    // toggleClass()   对集合中元素(实例化对象)的class名进行切换  (没有就新增 有就删除)
    // hasClass()      判断对集合中元素是否存在某个class名 (只要有一个存在则返回true)

    GetEle.prototype.addClass = function (classStr) {
        // 多class名之间用空格分隔  "one two three"
        var list = classStr.split(" "); // ["one","two","three"]
        this.each(function (ele, i) {
            ele.classList.add(...list);
        })
    }

    GetEle.prototype.removeClass = function (classStr) {
        // 多class名之间用空格分隔  "one two three"
        var list = classStr.split(" "); // ["one","two","three"]
        this.each(function (ele, i) {
            ele.classList.remove(...list);
        })
    }

    GetEle.prototype.toggleClass = function (classStr) {
        this.each(function (ele, i) {
            ele.classList.toggle(classStr);
        })
    }

    // 判断对集合中元素是否存在某个class名 (只要有一个存在则返回true)
    GetEle.prototype.hasClass = function (classStr) {
        var flag = false;//假设集合中不存在某个class名
        this.each(function (ele, i) {
            if (ele.classList.contains(classStr)) {
                flag = true;
            }
        })
        return flag;
    }

    // attr(key,val)   获取和设置集合中元素(实例化对象)的属性节点
    // attr("class","one")  有两个参数就设置
    // attr("class")        有一个参数就获取
    GetEle.prototype.attr = function (key, val) {
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        if (arguments.length >= 2) {
            this.each(function (ele) {
                ele.setAttribute(key, val);
            })
        } else if (arguments.length == 1) {
            return this[0].getAttribute(key);
        }
    }

    // prop(key,val)   获取和设置集合中元素(实例化对象)的属性
    // prop("className","one")  有两个参数就设置   ele.className ele.clientWidth ele.offsetLeft
    // prop("className")        有一个参数就获取
    GetEle.prototype.prop = function (key, val) {
        if (arguments.length >= 2) {
            this.each(function (ele) {
                ele[key] = val;
            })
        } else if (arguments.length == 1) {
            return this[0][key];
        }
    }

    GetEle.prototype.each = function (callback) { // 回调函数 => 每次循环时执行
        // this => 调用此方法的实例化对象 => 获取元素的集合(伪数组)
        for (var i = 0; i < this.length; i++) { // 传入一个回调函数 每次循环时执行
            var item = this[i]; // 循环的当前元素
            var index = i;  // 循环的当前下标
            callback(item, index); //两个实参 
        }
    }

    function $(selector) { // 接收css选择器 => 返回对应的实例化对象
        return new GetEle(selector);
    }

    $(".html").click(function () {
        $(".list li").html("123123");
    })

    $(".hide").click(function () {
        $(".list li").hide();
    })

    $(".show").click(function () {
        $(".list li").show();
    })

    $(".width").click(function () {
        // $(".list li").width("200px");
        $(".list li").width(200);
        console.log($(".list li").width());
    })

    $(".height").click(function () {
        // $(".list li").height("200px");
        $(".list li").height(200);
        console.log($(".list li").height());
    })

    $(".css").click(function () {
        $(".list").css("overflow", "hidden");

        // 每次设置一个css属性太麻烦 => 可以将设置的css属性名和属性值整合到对象中 => 传入一个对象参数
        // $(".list li").css("width", "100px");
        // $(".list li").css("height", "100px");
        // $(".list li").css("background", "green");
        // $(".list li").css("float", "left");

        $(".list li").css({
            width: "100px",
            height: "100px",
            background: "green",
            float: "left",
        })


        console.log($(".list li").css("width"));
        console.log($(".list li").css("height"));
        console.log($(".list li").css("backgroundColor"));
        console.log($(".list li").css("float"));

    })


    $(".addClass").click(function () {
        $(".list li").addClass("one two three four");
    })

    $(".removeClass").click(function () {
        $(".list li").removeClass("one two three four");
    })

    $(".toggleClass").click(function () {
        $(".list li:nth-child(1)").toggleClass("active");
    })
    $(".hasClass").click(function () {
        console.log($(".list li:nth-child(1)").hasClass("active"));
    })

    $(".attr").click(function () {
        $(".list li").attr("id", "demo");
        $(".list li").attr("class", "one");
        $(".list li").attr("title", "hello");
        $(".list li").attr("asd", "asd");
    })

    $(".prop").click(function () {
        $(".list li").prop("id", "demo");
        $(".list li").prop("className", "one");
        $(".list li").prop("title", "hello");
        console.log($(".list li").prop("clientWidth"));
        console.log($(".list li").prop("offsetLeft"));
    })







</script>

</html>